<!--
 * @Descripttion: 代码编辑器
 * @version: 1.0
 * @Author: sakuya
 * @Date: 2022年5月20日21:46:29
 * @LastEditors:
 * @LastEditTime:
-->

<template>
  <div class="sc-code-editor" :style="{ height: _height, width: '100%' }">
    <div ref="containerRef"></div>
  </div>
</template>
<script setup>
// //主题
// import 'codemirror/theme/idea.css'
// import 'codemirror/theme/darcula.css'

// //功能
// import 'codemirror/addon/selection/active-line'

import { computed, markRaw, onMounted, ref, toRefs, useTemplateRef } from 'vue'
const containerRef = useTemplateRef('containerRef')

const modelValue = defineModel({ type: String, default: '' })

import { Compartment, EditorState, StateEffect, StateField } from '@codemirror/state'
import { javascript, javascriptLanguage } from '@codemirror/lang-javascript'
import { sql } from '@codemirror/lang-sql'
import { EditorView, Decoration, ViewPlugin } from '@codemirror/view'
import { oneDark } from '@codemirror/theme-one-dark' //引入one dark 主题

import { basicSetup } from 'codemirror'

const props = defineProps({
  mode: {
    type: String,
    default: 'javascript'
  },
  height: {
    type: [String, Number],
    default: 400
  },
  options: {
    type: Object,
    default: () => {
      return {}
    }
  },
  theme: {
    type: String,
    default: 'dark'
  },
  readOnly: {
    type: Boolean,
    default: false
  },
  extensions: {
    type: Array,
    default: () => []
  }
})
const { mode, height, options, theme, readOnly, extensions } = toRefs(props)

let languageConf = new Compartment()
let tabSize = new Compartment()
let style = new Compartment()
let editable = new Compartment()
let themeConf = new Compartment()
const contentValue = ref('')

let langSetion = null
if (mode.value === 'sql') {
  langSetion = languageConf.of(sql())
} else {
  langSetion = languageConf.of(javascript())
}
let themeSetion = themeConf.of(oneDark)

const view = ref()
let heightPx = Number(height.value) + 'px'
const customTheme = EditorView.theme({
  '&': { height: heightPx, width: '100%' }
  // '.cm-scroller': { overflow: 'auto' }
})

onMounted(() => {
  let state = EditorState.create({
    doc: props.modelValue,
    // selection: EditorSelection.create([
    //   EditorSelection.range(0, 4),
    //   EditorSelection.cursor(5),
    // ]),
    extensions: [
      basicSetup,
      //  lineNumbers(),     //启用行号
      // docSizePlugin,
      // EditorState.allowMultipleSelections.of(true),
      langSetion,
      tabSize.of(EditorState.tabSize.of(8)),
      // style.of(EditorView.editorAttributes.of({ style: 'height:200px;width:100%' })),
      // 控制是否可编辑
      // 没有光标 不可选中 类似 disabled
      // editable.of(EditorView.editable.of(true)),
      // 只读 有光标 能选择 不能输入
      editable.of(EditorState.readOnly.of(false)),

      // 设置主题 第一个参数 根据选择器 设置样式 第二个参数对象 是否启用黑暗模式
      themeSetion,
      customTheme,
      EditorView.updateListener.of((update) => {
        cursor.value = update.state.selection.main.to
        if (update.docChanged) {
          modelValue.value = update.state.doc.toString()
          // emits('update:modelValue', update.state.doc.toString())
        }
        // else{
        //   cursor.value=  update.state.selection.main.to
        // }
      }),
      ...extensions.value
    ]
  })
  view.value = new EditorView({
    state,
    parent: containerRef.value
  })
})

const cursor = ref(0)
const coder = ref(null)
const opt = ref({
  theme: theme.value, //主题
  styleActiveLine: true, //高亮当前行
  lineNumbers: true, //行号
  lineWrapping: false, //自动换行
  tabSize: 4, //Tab缩进
  indentUnit: 4, //缩进单位
  indentWithTabs: true, //自动缩进
  mode: mode.value, //语言
  readOnly: readOnly.value, //只读
  ...options.value
})

const _height = computed(() => {
  return Number(height.value) ? Number(height.value) + 'px' : height.value
})
watch(modelValue, (newValue) => {
  if (newValue !== view.value.state.doc.toString()) {
    view.value.dispatch({
      changes: {
        from: 0,
        to: view.value.state.doc.length,
        insert: newValue
      }
    })
  }
})
const dispatch = (obj) => {
  view.value.dispatch(obj)
}
defineExpose({ dispatch })
</script>

<style scoped>
.sc-code-editor {
  font-size: 14px;
  border: 1px solid #ddd;
  line-height: 150%;
}
.sc-code-editor:deep(.CodeMirror) {
  height: 100%;
}
</style>
